import abc
import datetime
import functools

from coin.exchange.base.kr_rest.constants import get_rep_currency
from coin.exchange.base.kr_rest.from_str import FromStrCurrencyMixin


@functools.total_ordering
class NormCurrency(object):
  def __init__(self, currency):
    assert type(currency) == str
    # most of currency are A-Z only, except binance's 123,456.
    assert currency.isupper() or currency.isdigit()
    self._currency = currency
    self._short_symbol = currency

  def __str__(self):
    return self._short_symbol

  def __repr__(self):
    return repr(dict(currency=self.currency))

  def __hash__(self):
    return hash(str(self))

  def __eq__(self, other):
    if type(other) == str:
      return str(self) == other or self.symbol == other
    elif isinstance(other, NormCurrency):
      return str(self) == str(other)
    else:
      raise TypeError("Not supported: %s" % (other,))

  def __lt__(self, other):
    assert isinstance(other, NormCurrency), type(other)
    return str(self) < str(other)

  @property
  def currency(self):
    return self._currency

  @property
  def short_symbol(self):
    return self._short_symbol

  @property
  def symbol(self):
    return self._short_symbol


class NativeCurrency(NormCurrency, FromStrCurrencyMixin):
  __metaclass__ = abc.ABCMeta

  # currency is not product, but for lazy user.
  _product_type = "Currency"
  _exchange = "UNKNOWN"

  def __init__(self, currency, native_currency):
    assert isinstance(currency, str)
    assert isinstance(native_currency, str)
    super().__init__(currency=currency)
    self._rep_currency = get_rep_currency(currency)
    self._native_currency = native_currency
    self._full_symbol = "%s.%s.%s" % (self._currency, self.exchange, self._native_currency)

  def __str__(self):
    return self._full_symbol

  def __repr__(self):
    return repr(
        dict(exchange=self.exchange, currency=self.currency, native_currency=self.native_currency))

  @property
  def product_type(self):
    return type(self)._product_type

  @property
  def exchange(self):
    return type(self)._exchange

  @property
  def rep_currency(self):
    return self._rep_currency

  @property
  def native_currency(self):
    return self._native_currency

  @property
  def native_symbol(self):
    return self._native_currency

  @property
  def full_symbol(self):
    return self._full_symbol

  def assert_component(self, currency, native_currency):
    assert self.currency == currency, (self.currency, currency)
    assert self.native_currency == native_currency, (self.native_currency, native_currency)

  def assert_component2(self, exchange, currency, native_currency):
    assert self.exchange == exchange, (self.exchange, exchange)
    assert self.currency == currency, (self.currency, currency)
    assert self.native_currency == native_currency, (self.native_currency, native_currency)

  @classmethod
  def FromCurrency(cls, currency, current_datetime=None):
    assert isinstance(currency, NormCurrency), (type(currency), currency)
    current_datetime = current_datetime or datetime.datetime.now()
    currency = cls.FromStrCurrency(currency.currency, current_datetime)
    return currency
